<template>
  <div class="skybox">
    <div class="title">
      太阳系
    </div>
    <div ref="refDom" class="model-view">
    </div>
  </div>
</template>

<script setup>
import {useTemplateRef} from 'vue'
import * as THREE from 'three'
import {OrbitControls} from 'three/examples/jsm/controls/OrbitControls.js'
import Stats from 'three/examples/jsm/libs/stats.module.js'

defineOptions({
  name: 'sunSystem'
})
const refDom = useTemplateRef('refDom')
let scene, // 场景
    camera, // 相机
    renderer,// 渲染器
    controls,// 控制器
    frameStats // 性能监视器
// 纹理加载器
const loader = new THREE.TextureLoader()
let DOM
let solarSystem,// Object3D太阳系
    earthSystem //Object3D地月系
onMounted(() => {
  DOM = refDom.value
  initScene()
  initCamera()
  initRender()
  initControls()
  initLight()
  initFrameStats()
  drawModel()
  window.onresize = () => {
    renderer.setSize(DOM.clientWidth, DOM.clientHeight)
    camera.aspect = DOM.clientWidth / DOM.clientHeight
    camera.updateProjectionMatrix()// 更新相机的投影矩阵
  }
  render()
  // initHelpers()
})

const initScene = () => {
  scene = new THREE.Scene()
  sceneBG()
}
const initCamera = () => {
// 透视摄像机
  camera = new THREE.PerspectiveCamera(40, DOM.clientWidth / DOM.clientHeight, 0.1, 1000)
  camera.position.set(0, 0, 50)
  camera.lookAt(0, 0, 0)
}
const initRender = () => {
  renderer = new THREE.WebGLRenderer({
    antialias: true, // 抗锯齿
    alpha: true//背景色透明
  })
  renderer.setPixelRatio(window.devicePixelRatio)
  renderer.toneMapping = THREE.ACESFilmicToneMapping
  renderer.toneMappingExposure = 1
  renderer.setSize(DOM.clientWidth, DOM.clientHeight)
  DOM.appendChild(renderer.domElement)
}

// 创建方形纹理
const generateSprite = () => {
  const canvas = document.createElement('canvas')
  canvas.width = 16
  canvas.height = 16
  const context = canvas.getContext('2d')
// 创建颜色渐变
  const gradient = context.createRadialGradient(
      canvas.width / 2,
      canvas.height / 2,
      0,
      canvas.width / 2,
      canvas.height / 2,
      canvas.width / 2
  )
  gradient.addColorStop(0, 'rgba(255,255,255,1)')
  gradient.addColorStop(0.2, 'rgba(0,255,255,1)')
  gradient.addColorStop(0.4, 'rgba(0,0,64,1)')
  gradient.addColorStop(1, 'rgba(0,0,0,1)')
// 绘制方形
  context.fillStyle = gradient
  context.fillRect(0, 0, canvas.width, canvas.height)
// 转为纹理
  const texture = new THREE.Texture(canvas)
  texture.needsUpdate = true
  return texture
}

//星空背景绘制
const sceneBG = () => {
  const positions = []
  // 创建 几何体
  const geometry = new THREE.BufferGeometry()
  for (let i = 0; i < 10000; i++) {
    let vertex = new THREE.Vector3()
    vertex.x = Math.random() * 2 - 1
    vertex.y = Math.random() * 2 - 1
    vertex.z = Math.random() * 2 - 1
    positions.push(vertex.x, vertex.y, vertex.z)
  }
  // 对几何体 设置 坐标 和 颜色
  geometry.setAttribute('position', new THREE.Float32BufferAttribute(positions, 3))
  // 默认球体
  geometry.computeBoundingSphere()

  // ------------- 1 ----------
  // 星星资源图片
  // ParticleBasicMaterial 点基础材质
  const starsMaterial = new THREE.PointsMaterial({
    map: generateSprite(),
    size: 2,
    transparent: true,
    opacity: 1,
    //true：且该几何体的colors属性有值，则该粒子会舍弃第一个属性--color，而应用该几何体的colors属性的颜色
    // vertexColors: true,
    blending: THREE.AdditiveBlending,
    sizeAttenuation: true
  })
  // 粒子系统 网格
  let stars = new THREE.Points(geometry, starsMaterial)
  stars.scale.set(300, 300, 300)
  scene.add(stars)
}

// 控制器
const initControls = () => {
  controls = new OrbitControls(camera, renderer.domElement) // 鼠标控制
  controls.enableDamping = true // 设置阻尼
  controls.minDistance = 1
  controls.maxDistance = 100
  controls.update()
}

const initLight = () => {
  scene.add(new THREE.PointLight(0xffffff, 500)) // 点光源
  scene.add(new THREE.AmbientLight(0xFFFFFF, 0.05)) // 环境光
  // scene.add(new THREE.DirectionalLight(0xFFFFFF, 0.1)) // 平行光
}
// 创建性能监视器
const initFrameStats = () => {
  frameStats = new Stats()
  frameStats.setMode(0)// 设置监视器面板，传入面板id（0: fps, 1: ms, 2: mb）
  // 设置监视器位置
  frameStats.domElement.style.position = 'absolute'
  frameStats.domElement.style.left = '0px'
  frameStats.domElement.style.top = '0px'
  // 将监视器添加到页面中
  DOM.appendChild(frameStats.domElement)
}
const initHelpers = () => {
  scene.add(new THREE.AxesHelper(100)) // xyz轴辅助线
  // scene.add(new THREE.GridHelper(50, 100, 0x135f9e, 0x135f9e)) // 地板网格辅助线
  const planeSize = 50
  const loader = new THREE.TextureLoader()
  const texture = loader.load(`${import.meta.env.VITE_THREEJS_BASE_URL}/images/checker.png`)
  // THREE.RepeatWrapping 纹理重复
  texture.wrapS = THREE.RepeatWrapping
  texture.wrapT = THREE.RepeatWrapping
  // 当一个纹素覆盖大于一个像素时，贴图将如何采样。 THREE.NearestFilter，它将使用最接近的纹素的值。
  texture.magFilter = THREE.NearestFilter
  const repeats = planeSize
  // 重复次数
  texture.repeat.set(repeats, repeats)
  const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize)
  // Phong材质
  const planeMat = new THREE.MeshPhongMaterial({
    map: texture,
    side: THREE.DoubleSide
  })
  const mesh = new THREE.Mesh(planeGeo, planeMat)
  mesh.rotation.x = Math.PI * -0.5
  scene.add(mesh)
}
const render = () => {
  solarSystem?.rotateY(Math.PI / 180)
  earthSystem?.rotateY(0.01)
  frameStats.update()// 更新帧数
  renderer.render(scene, camera)
  requestAnimationFrame(render)
}

// 太阳系模型
const drawModel = () => {
  // 太阳系 物体对象
  solarSystem = new THREE.Object3D()
  earthSystem = new THREE.Object3D()
  const sphereGeometry = new THREE.SphereGeometry(1.5, 36, 36) // 球体
  // 太阳
  console.log(import.meta.env.VITE_THREEJS_BASE_URL)
  const sunTexture = loader.load(`${import.meta.env.VITE_THREEJS_BASE_URL}/sun/1.jpg`)
  const sunMaterial = new THREE.MeshBasicMaterial({
    map: sunTexture
  })
  sunMaterial.color.set('#ffff00')
  const sunMesh = new THREE.Mesh(sphereGeometry, sunMaterial)
  sunMesh.scale.set(3, 3, 3)// 放大3倍
  // scene.add(sunMesh) //屏蔽掉太阳加入全局场景

  // 地球
  const earthTexture = loader.load(`${import.meta.env.VITE_THREEJS_BASE_URL}/sun/3.jpg`)
  const earthMaterial = new THREE.MeshPhongMaterial({
    map: earthTexture
  })
  const earthMesh = new THREE.Mesh(sphereGeometry, earthMaterial)
  // earthMesh.position.x = 20
  // scene.add(earthMesh) //屏蔽掉地球加入全局场景

  // 月球
  const moonTexture = loader.load(`${import.meta.env.VITE_THREEJS_BASE_URL}/sun/2.jpg`)
  const moonMaterial = new THREE.MeshPhongMaterial({
    map: moonTexture
  })
  moonMaterial.color.set('#f30')
  const moonMesh = new THREE.Mesh(sphereGeometry, moonMaterial)
  moonMesh.position.x = 3
  moonMesh.scale.set(0.4, 0.4, 0.4)// 放大3倍
  // scene.add(moonMesh) //屏蔽掉 地球加入全局场景

  solarSystem.add(sunMesh) //  将太阳加入太阳系
  const sunMeshClone1 = sunMesh.clone() // 克隆 太阳1
  const sunMeshClone2 = sunMesh.clone() // 克隆 太阳2
  const sunMeshClone3 = sunMesh.clone() // 克隆 太阳3
  const sunMeshClone4 = sunMesh.clone() // 克隆 太阳4
  sunMeshClone1.scale.set(0.4, 0.4, 0.4)
  sunMeshClone2.scale.set(0.4, 0.4, 0.4)
  sunMeshClone3.scale.set(0.4, 0.4, 0.4)
  sunMeshClone4.scale.set(0.4, 0.4, 0.4)
  sunMeshClone1.position.x = 6
  sunMeshClone2.position.x = -6
  sunMeshClone3.position.z = 6
  sunMeshClone4.position.z = -6
  solarSystem.add(sunMeshClone1)
  solarSystem.add(sunMeshClone2)
  solarSystem.add(sunMeshClone3)
  solarSystem.add(sunMeshClone4)
  solarSystem.add(earthMesh) // 将地球加入太阳系
  earthSystem.add(earthMesh) // 将地球加入地月系
  earthSystem.add(moonMesh) // 将月球加入地月系
  earthSystem.position.x = 15
  solarSystem.add(earthSystem) // 将地月系加入太阳系
  scene.add(solarSystem)
}

</script>

<style lang="scss" scoped>
.skybox {
  width: 100%;
  height: 100%;

  .title {
    height: 30px;
    line-height: 30px;
    margin-bottom: 10px;
  }

  .model-view {
    position: relative;
    width: 100%;
    height: calc(100% - 40px);
    background: #000;
  }
}
</style>
